/*****************************************************************************
* Project:
* All project
* Copyright(C) 2020-2021 Shenju Co.Ltd, All rights reserved.
* Description:
* All code is genrated by yangyang
* Author:
* yangyang
****************************************************************************/

#include "ff.h"
#include "db_api.h"

#include <stdio.h>
#include <mmsystem.h>
#include <malloc.h>
#include <assert.h>
#include <tchar.h>

FATFS gFATFS ={0, 0, 0, 0, 0, 0, 0, 256};

#define OAL_MAX_PATH 260

FRESULT f_open (FIL* fp, const char* path, unsigned char mode)
{
#ifdef UNICODE
	wchar_t widestr[OAL_MAX_PATH];
	MultiByteToWideChar(CP_UTF8, 0, path, -1, widestr, OAL_MAX_PATH);
#else
	char widestr[OAL_MAX_PATH];
	strcpy(widestr, path);
#endif

	fp->handle = CreateFile(widestr,
		((mode&FA_READ)?GENERIC_READ:0) | ((mode&FA_WRITE)?GENERIC_WRITE:0),
		0,                                
		NULL,
		((mode&FA_CREATE_NEW)?CREATE_NEW:(((mode&FA_CREATE_ALWAYS)?CREATE_ALWAYS:(((mode&FA_OPEN_ALWAYS)?OPEN_ALWAYS:OPEN_EXISTING))))),
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	if(fp->handle == INVALID_HANDLE_VALUE )
	{
		return FR_DISK_ERR;
	}

	return FR_OK;
}

FRESULT f_close (FIL* fp)
{
	if(!CloseHandle(fp->handle)){
		return FR_DISK_ERR;
	}
	return FR_OK;
}

FRESULT f_read (FIL* fp, void* buff, unsigned int btr, unsigned int* br)
{
	if(! ReadFile((LPDWORD)fp->handle, buff, btr, (LPDWORD)br, NULL)){
		if(GetLastError() == ERROR_HANDLE_EOF)
			return FR_INVALID_PARAMETER;
	}

	if(*br > btr)	//patch for unsafe file system
		*br = 0;

	return FR_OK;
}

FRESULT f_write (FIL* fp, const void* buff, unsigned int btw, unsigned int* bw)
{
	if(! WriteFile((LPDWORD)fp->handle, buff, btw, (LPDWORD)bw, NULL)){
		if(GetLastError() == ERROR_HANDLE_EOF)
			return FR_INVALID_PARAMETER;
	}

	if(*bw > btw)	//patch for unsafe file system
		*bw = 0;

    return FR_OK;
}

FRESULT f_lseek (FIL* fp, long long ofs)
{
	unsigned int ret = 0;
	LONG highMove = 0;
	if (ofs < 0)
	{
		highMove = -1;
	}
	ret = SetFilePointer((LPDWORD)fp->handle, (long)ofs, &highMove, 0);
	if(INVALID_SET_FILE_POINTER == ret)
	{
		return FR_INVALID_PARAMETER;
	}
	return FR_OK;
}

FSIZE_t f_size(FIL* fp)
{
	int size = GetFileSize((LPDWORD)fp->handle, NULL);
	if (size == INVALID_FILE_SIZE)
	{
		return 0;
	}
	return (FSIZE_t)size;
}

FSIZE_t f_tell(FIL* fp)
{
	unsigned int ret = SetFilePointer((LPDWORD)fp->handle, 0, NULL, FILE_CURRENT);
	if (INVALID_SET_FILE_POINTER == ret)
	{
		return 0;
	}
	return (FSIZE_t)ret;
}

FRESULT f_mkdir (const char* path)
{
	int res;
	SECURITY_ATTRIBUTES attrib;
	attrib.bInheritHandle = FALSE;
	attrib.lpSecurityDescriptor = NULL;
	attrib.nLength = sizeof(SECURITY_ATTRIBUTES);
	res = CreateDirectoryA(path, &attrib);
	if(res == 1)
		return FR_OK;
	else
		return FR_EXIST;
}

FRESULT f_unlink (const char* path)
{
	int ret = 0;
#ifdef UNICODE
	wchar_t wstr[MAXPATH] = {0};
	char tmp[MAXPATH] = {0};
	MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)path, -1, wstr, MAXPATH);
	WideCharToMultiByte(CP_ACP, 0, wstr, -1, tmp, MAXPATH, NULL, FALSE);
	ret = remove(tmp);
#else
	ret = remove(path);
#endif

	if(ret == 0)
	{
		return FR_OK;
	}
	return FR_INT_ERR;
}

FRESULT f_rename (const char* path_old, const char* path_new)
{
	int ret = rename(path_old, path_new);
	if(ret == 0)
	{
		return FR_OK;
	}
	return FR_INT_ERR;
}

FRESULT f_stat(const char* path, FILINFO* fno)
{
	DWORD attr = GetFileAttributesA(path);
	fno->fattrib = 0;

	if (attr  == 0xffffffff)
	{
		return FR_NO_FILE;
	}

	if(attr & FILE_ATTRIBUTE_READONLY)
	{
		fno->fattrib |= AM_RDO;
	}
	
	if(attr & FILE_ATTRIBUTE_HIDDEN)
	{
		fno->fattrib |= AM_HID;
	}
	
	if(attr & FILE_ATTRIBUTE_SPARSE_FILE)
	{
		fno->fattrib |= AM_SYS;
	}

	if(attr & FILE_ATTRIBUTE_DIRECTORY)
	{
		fno->fattrib |= AM_DIR;
	}
	
	if(attr & FILE_ATTRIBUTE_ARCHIVE)
	{
		fno->fattrib |= AM_ARC;
	}

	return FR_OK;
}

FRESULT f_closedir (DIR* dp)
{
	int ret = FindClose(dp->hFind);
	if(ret)
	{
		dp->hFind = INVALID_HANDLE_VALUE;
		return FR_OK;
	}
	else
	{
		return FR_INT_ERR;
	}
}

FRESULT f_findfirst (DIR* dp, FILINFO* fno, const char* path, const char* pattern)
{
#ifdef UNICODE
	char tmp[MAXPATH] = {0};
	wchar_t findstr[MAXPATH] = {0};
	sprintf(tmp, "%s\\%s", path, pattern);
	MultiByteToWideChar(CP_UTF8, 0, tmp, -1, findstr, OAL_MAX_PATH);
#else
	char findstr[MAXPATH] = {0};
	sprintf(findstr, "%s\\%s", path, pattern);
#endif
	dp->hFind = FindFirstFile(findstr, &dp->FindData);

	if(INVALID_HANDLE_VALUE != dp->hFind)
	{
#ifdef UNICODE
		char tmp[MAXPATH] = {0};
		WideCharToMultiByte(CP_UTF8, 0, dp->FindData.cFileName, -1, tmp, MAXPATH, NULL, FALSE);
		strcpy(fno->fname, tmp);
#else
		strcpy(fno->fname, dp->FindData.cFileName);
#endif

		fno->fattrib = 0;

		if(dp->FindData.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
		{
			fno->fattrib |= AM_RDO;
		}

		if(dp->FindData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
		{
			fno->fattrib |= AM_HID;
		}

		if(dp->FindData.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE)
		{
			fno->fattrib |= AM_SYS;
		}

		if(dp->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
		{
			fno->fattrib |= AM_DIR;
		}

		if(dp->FindData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
		{
			fno->fattrib |= AM_ARC;
		}

		return FR_OK;
	}
	else
	{
		return FR_NO_PATH;
	}
}

FRESULT f_findnext (DIR* dp, FILINFO* fno)
{
	int ret = FindNextFile(dp->hFind, &dp->FindData);
	if(ret)
	{
#ifdef UNICODE
		char tmp[MAXPATH];
		WideCharToMultiByte(CP_UTF8, 0, dp->FindData.cFileName, -1, tmp, MAXPATH, NULL, FALSE);
		strcpy(fno->fname, tmp);
#else
		strcpy(fno->fname, dp->FindData.cFileName);
#endif

		fno->fsize = dp->FindData.nFileSizeHigh;
		fno->fsize <<= 32;
		fno->fsize += dp->FindData.nFileSizeLow;

		fno->fattrib = 0;

		if(dp->FindData.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
		{
			fno->fattrib |= AM_RDO;
		}

		if(dp->FindData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
		{
			fno->fattrib |= AM_HID;
		}

		if(dp->FindData.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE)
		{
			fno->fattrib |= AM_SYS;
		}

		if(dp->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
		{
			fno->fattrib |= AM_DIR;
		}

		if(dp->FindData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
		{
			fno->fattrib |= AM_ARC;
		}

		return FR_OK;
	}
	else
	{
		fno->fname[0] = 0;
		return FR_OK;
	}
}

FRESULT f_sync (FIL* fp)/* Flush cached data of the writing file */
{
	if(FlushFileBuffers(fp->handle))
		return FR_OK;
	else
		return FR_INT_ERR;
}

DWORD GetDirSize(const TCHAR * dir)
{
	HANDLE hFind;
	WIN32_FIND_DATA FindData;
	DWORD DirSize = 0;
	
	TCHAR path[MAXPATH];
	TCHAR findstr[MAXPATH];
	wsprintf(findstr, _T("%s\\*"), dir);

	hFind = FindFirstFile(findstr, &FindData);
	if(INVALID_HANDLE_VALUE == hFind)return 0;

	do{
		if(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
		{
			if(_tcscmp(FindData.cFileName, _T(".")) == 0)	continue;
			if(_tcscmp(FindData.cFileName, _T("..")) == 0)	continue;
			{
				wsprintf(path, _T("%s\\%s"), dir, FindData.cFileName);
				DirSize += GetDirSize(path);
			}
		}
		else
		{
			unsigned long long size = FindData.nFileSizeHigh;
			size <<= 32;
			size += FindData.nFileSizeLow;
			size = (size + (gFATFS.csize*512) - 1) / (gFATFS.csize*512);
			DirSize += (DWORD)size;
		}

	}while(FindNextFile(hFind, &FindData));

	return DirSize;
}

FRESULT f_getfree (const char* path, DWORD* nclst, FATFS** fatfs) /* Get number of free clusters on the drive */
{
	TCHAR widestr[MAXPATH] = {0};
#ifdef UNICODE
	MultiByteToWideChar(CP_ACP, 0, path, -1, widestr, MAXPATH);
#else
	strcpy(widestr, path);
#endif
	*fatfs = &gFATFS;
	*nclst = 4*1024*1024 - GetDirSize(widestr);
	return FR_OK;
}
